#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright 2007 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#


import wsgiref.handlers


from google.appengine.ext import webapp
from google.appengine.api import urlfetch
from google.appengine.ext import db
from xml.etree import ElementTree as ET
import datetime
from string import Template
import urllib
import logging
import re

douban_kml_url = 'http://ditu.google.com/maps/ms?ie=UTF8&msa=0&output=kml&msid=103335311190703792834.00044d70e92741170f3d3'
#douban_kml_url = 'http://swordsp.googlepages.com/helpwenchuan.kml'


#class Region(db.Model):
#  name = db.StringProperty()
#  upper = db.SelfReferenceProperty()


class DataSource(db.Model):
  name = db.StringProperty()


class InfoItem(db.Model):
  name = db.StringProperty()
  desc = db.TextProperty()
  style = db.StringProperty()
  #region = db.ReferenceProperty(Region)
  date = db.DateTimeProperty()
  tags = db.StringListProperty()
  geoinfo = db.ListProperty(float)
  source = db.ReferenceProperty(DataSource)


class MapQuery(object):
  regions = set([
    u"北川县",
    u"汶川县",
    u"青川县",
    u"理县",    
    u"茂县",
    u"安县",
    u"成都市",
    u"绵竹市",    
    u"德阳市",    
    u"绵阳市",
    u"江油市",
    u"什邡市",
    u"彭州市",
    u"广元市",
    u"都江堰市",
    u"阿坝州"
  ])
  
  def __init__(self, parameters):
    self.conditions = []
    if parameters.has_key('tags'):
      self.tags = set(parameters['tags']);
      self.conditions.append(
        lambda item: self.tags.issubset(item.tags)
      )
    if parameters.has_key('keywords'):
      self.keywords = parameters['keywords'];
      for keyword in self.keywords:
        self.conditions.append(
          lambda item: keyword in item.name or keyword in item.desc
        )
    if parameters.has_key('region'):
      self.region = parameters['region'];
      if self.region in self.regions:
        self.conditions.append(
          lambda item: self.region in item.tags
        )
      elif self.region == u'其它地区':
        self.conditions.append(
          lambda item: not self.regions.intersection(item.tags)
        )        
        
    if parameters.has_key('date_start'):
      self.date_start = time.strptime(parameters['date_start'], '%Y-%m-%d')
      self.conditions.append(lambda self, item: self.date_start <= item.date)
    if parameters.has_key('date_end'):
      self.date_end = time.strptime(parameters['date_end'], '%Y-%m-%d')
      self.conditions.append(lambda self, item: self.date_end >= item.date)
    
  def match(self, item):
    for condition in self.conditions:
      if not condition(item):
        return False
    return True
  
  def to_gql(self):
    pass


class KMLWriter(object):
  header_tmpl = Template(u'''\
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://earth.google.com/kml/2.2">
<Document>
  <name>灾区最新物资人员供需图</name>
  <description><![CDATA[【灾情】灾区最新物资人员供需图。征志愿者（24小时随时更新）投递信息请联系minjie.qian@gmail.com或者dengdongyang@gmail.com。免责声明：所有信息来自网络，请鉴别使用。http://www.douban.com/group/topic/3213571/

#用户手册#
图例：
　　人员被困，紧急求援：红色感叹号
　　医疗救援：红十字
　　水和食物：水瓶和苹果
　　帐篷：绿色三角形
　　各类物资：篮子
　　车辆：货车
　　工程人员：扳手
　　志愿者：旗帜 
]]></description>
''')
  footer_tmpl = Template(u'''\
</Document>
</kml>
''')
  style_tmpl = Template(u'''\
  <Style id="style$id">
    <IconStyle>
      <Icon>
        <href>$iconUrl</href>
      </Icon>
    </IconStyle>
  </Style>
''')
  mark_tmpl = Template(u'''\
  <Placemark>
    <name>$name</name>
    <description><![CDATA[$desc]]></description>
    <styleUrl>#style$style_id</styleUrl>
    <Point>
      <coordinates>$lng,$lat,$alt</coordinates>
    </Point>
  </Placemark>
''')
  
  def __init__(self, items):
    self.items = items
    style_count = 0
    self.styles = {}
    for item in items:
      if item.style not in self.styles:
        style_count += 1
        self.styles[item.style] = style_count
    
  def write(self, output):
    output.write(self.header_tmpl.substitute())
    for style in self.styles:
      self.write_style(output, style)
    for item in self.items:
      self.write_mark(output, item)
    output.write(self.footer_tmpl.substitute())
    
  def write_style(self, output, style):
    output.write(self.style_tmpl.substitute(id=self.styles[style], iconUrl=style))
  
  def write_mark(self, output, item):
    output.write(self.mark_tmpl.substitute(
      name=item.name, desc=item.desc, style_id=self.styles[item.style],
      lng=item.geoinfo[0], lat=item.geoinfo[1], alt=item.geoinfo[2]))


class JSONWriter(object):
  _rev_escapes = {'\\': '\\\\',
                  '\n': '\\n',
                  '\t': '\\t',
                  '\b': '\\b',
                  '\r': '\\r',
                  '\f': '\\f',
                  '"': '\\"',
                  '\'': '\\\''
                  }
  
  def __init__(self, items, callback):
    self.items = items
    self.callback = callback
    
  def write(self, out):
    if self.callback:
      out.write(self.callback + '(')
    self.write_items(out, self.items)
    if self.callback:
      out.write(')')

  def write_items(self, out, items):
    out.write('[')
    count = 0
    for item in items:
      if count > 0:
        out.write(',')
      self.write_item(out, item)
      count += 1
    out.write(']')
        
  def write_item(self, out, item):
    out.write('{')
    out.write('name:\'' + self.escape(item.name) + '\',')
    out.write('desc:\'' + self.escape(item.desc) + '\',')
    out.write('geoinfo:' + str(item.geoinfo))
    out.write('}')
    
  def escape(self, text):
    return text.replace('\\', '\\\\').replace('\"', '\\\"').replace('\'', '\\\'')\
               .replace('\n', '\\n').replace('\t', '\\t').replace('\r', '\\r')\
               .replace('\b', '\\b').replace('\f', '\\f')
    

class MapManager(object):

  def __init__(self):
    self.items = []

  def import_kml(self, url, source_id):
    resp = urlfetch.fetch(url=url)
    logging.info('fetch ' + str(len(resp.content)) + ' bytes from ' + url)
    map = ET.fromstring(resp.content)
    ns = '{' + self.extract_namespace(map.tag)[0] + '}'
    document = map.find('.//' + ns + 'Document')
    
    styles = {}
    for style in map.findall('.//' + ns + 'Style'):
      styles[style.attrib['id']] = style.findtext('.//' + ns + 'href')
      
    
    source = self.get_source(source_id)
    new_items = []
    for mark in map.findall('.//' + ns + 'Placemark'):
      item = InfoItem(parent=source)
      item.name = mark.findtext(ns + 'name')
      item.desc = mark.findtext(ns + 'description')
      item.style = styles[mark.findtext(ns + 'styleUrl')[1:]]
      geoinfos = mark.findtext(ns + 'Point/' + ns + 'coordinates').split(',')
      item.geoinfo = [float(geoinfos[0]), float(geoinfos[1]), float(geoinfos[2])]
      item.tags = re.findall('\[([^\[\]]*)\]', item.desc)
      item.source = source
      new_items.append(item)
    logging.info(str(len(new_items)) + ' new items found.')
    self.update_items(new_items, source)

  def get_source(self, source_id):
    result = DataSource().all().filter('name =', source_id).fetch(1)
    if not result:
      source = DataSource(name=source_id)
      db.put(source)
      return source
    else:
      return result[0]
    
  def load_items(self):
    self.items = list(InfoItem.all())
    logging.info(str(len(self.items)) + ' items loaded.')
    
  def extract_namespace(self, tag):
    if tag[0] == '{':
      return tag[1:].split("}")
    else:
      return None, tag
    
  def update_items(self, new_items, source):
    expired_items = []
    if source:
      #expired_items = InfoItem.all().ancestor(source).fetch(500)
      query = db.GqlQuery('SELECT * FROM InfoItem WHERE source = :1', source)
      for result in query:
        expired_items.append(result)
    db.run_in_transaction(self.replace_items, new_items, expired_items)
    
  def replace_items(self, new_items, expired_items):
    if expired_items:
      for item in expired_items:
        item.delete()
      logging.info(str(len(expired_items)) + ' items expired.')
    db.put(new_items)
    logging.info(str(len(new_items)) + ' items saved.')
    
  def get_items(self, **params):
    query = MapQuery(params)
    return [item for item in self.items if query.match(item)]
  
  def get_jsonp(self, items):
    pass
  
  def output(self, out, **params):
    items = self.get_items(**params)
    if params['output'] == 'kml':
      writer = KMLWriter(items)
    elif params['output'] == 'json' or params['output'] == 'jsonp':
      writer = JSONWriter(items, params['callback'])
    writer.write(out)


#class HelpWenChuanMapManager(KMLManager):
#  self.url = ""
#
#  def __init__(self, url):
#    pass

class MapServiceHandler(webapp.RequestHandler):

  def get(self):    
    output = self.request.get('output')
    callback = self.request.get('callback')
    region = self.request.get('region')
    tags = []    
    if self.request.get('tags'):
      tags_unicode = self.request.get('tags')
      logging.info(tags_unicode)
      tags = tags_unicode.split()
    keywords = []
    if self.request.get('keywords'):
      keywords_unicode = self.request.get('keywords')
      logging.info(keywords_unicode)
      keywords = keywords_unicode.split()
    logging.info('output = ' + str(output))
    logging.info('callback = ' + str(callback))
    logging.info('tags = ' + str(tags))
    logging.info('keywords = ' + str(keywords))
    logging.info('region = ' + region)
    
    if output == 'kml':
      self.response.headers['Content-Type'] =	'application/vnd.google-earth.kml+xml; charset=utf-8'
    elif output == 'json':
      self.response.headers['Content-Type'] =	'application/json; charset=utf-8'
    elif output == 'jsonp':
      self.response.headers['Content-Type'] =	'application/jsonp; charset=utf-8'
    manager.output(out=self.response.out,
                   output=output,
                   tags=tags,
                   keywords=keywords,
                   region=region,
                   callback=callback)


manager = MapManager()
#manager.import_kml(douban_kml_url, 'helpwenchuan')
manager.load_items()

def main():
  application = webapp.WSGIApplication([('/helpwenchuan/mapservice', MapServiceHandler)],
                                       debug=False)
  wsgiref.handlers.CGIHandler().run(application)


if __name__ == '__main__':
  main()
